package com.behelpful.common.core.invocation;

import com.behelpful.common.core.exceptions.IRuntimeException;
import com.behelpful.common.core.result.ErrorResult;
import com.behelpful.common.core.utils.ExceptionUtil;
import com.behelpful.common.core.utils.ICollUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 代理工具类
 *
 * @author Alay
 * @date 2021-12-04 09:43
 * <T> 被代理的类
 */
@Slf4j
public abstract class IProxy<T> implements InvocationHandler, MethodInterceptor {
    /**
     * 目标对象
     */
    protected final T target;
    /**
     * 代理方法执行参数(可自定义修改参数)
     */
    protected Object[] args;

    /**
     * JDK 动态代理中多继承,不同的接口不同的 Method
     */
    protected Map<Class<?>, Set<Method>> superMethodMap;

    public IProxy(T target) {
        this.target = target;
    }

    /**
     * jdk 动态代理中映射 接口类与接口方法
     *
     * @param target
     * @return
     */
    public static Map<Class<?>, Set<Method>> superMethod(Object target) {
        Class<?>[] interfaces = target.getClass().getInterfaces();
        if (interfaces.length == 0) {
            // 代理类没有实现的接口
            throw ExceptionUtil.create(IRuntimeException.class, ErrorResult.NO_INTERFACE_ERR);
        }
        HashMap<Class<?>, Set<Method>> clazzMethodMap = new HashMap<>(1 << 2);
        for (Class<?> anInterface : interfaces) {
            Method[] methods = anInterface.getMethods();
            clazzMethodMap.put(anInterface, ICollUtil.asSet(methods));
        }
        return clazzMethodMap;
    }

    /**
     * JDK 动态代理,target 必须是有已经实现的接口
     *
     * @param target
     * @param proxy
     * @param <P>
     * @return
     */
    public static <T, P extends InvocationHandler> Object jdkProxy(T target, P proxy) {
        Class<?> targetClazz = target.getClass();
        Class<?>[] interfaces = targetClazz.getInterfaces();
        if (interfaces.length == 0) {
            // 代理类没有实现的接口
            log.error(ErrorResult.NO_INTERFACE_ERR.getMessage());
            throw ExceptionUtil.create(IRuntimeException.class, ErrorResult.NO_INTERFACE_ERR);
        }
        return Proxy.newProxyInstance(targetClazz.getClassLoader(), interfaces, proxy);
    }

    /**
     * Spring 的 cglib 动态代理
     *
     * @param target
     * @param proxy
     * @param <T>
     * @return
     */
    public static <T, P extends MethodInterceptor> T cglibProxy(T target, P proxy) {
        // 创建一个增强器对象==帮助我们生成Cglib代理对象
        Enhancer enhancer = new Enhancer();
        // 设置你对哪个类进行增强
        enhancer.setSuperclass(target.getClass());
        // 设置你的增强
        enhancer.setCallback(proxy);
        return (T) enhancer.create();
    }

    /**
     * JDK 代理方法
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (null == this.args) {
            this.args = args;
        }
        return this.jdkInvoke(proxy, method, this.args);
    }


    /**
     * cglib 代理执行方法
     *
     * @param proxy       代理对象
     * @param method      代理执行的方法
     * @param args        代理方法执行的参数
     * @param methodProxy 调用方法的代理方法的反射对象
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if (null == this.args) {
            this.args = args;
        }
        return this.cglibInvoke(proxy, method, this.args, methodProxy);
    }

    /**
     * JDK动态代理默认方法
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    protected Object jdkInvoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 默认不进行代理执行
        Object result;
        try {
            // 前置通知
            log.info("前置通知");
            // 执行所代理的目标方法调用
            result = method.invoke(this.target, args);
            // 后置通知
            log.info("后置通知");
        } catch (Exception e) {
            // 异常通知
            log.error("异常通知");
            throw e;
        }
        return result;
    }


    /**
     * cglib 代理默认执行方法
     *
     * @param proxy
     * @param method
     * @param args
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    protected Object cglibInvoke(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result;
        try {
            // 前置通知
            log.info("前置通知");
            // 执行所代理的目标方法调用
            result = methodProxy.invokeSuper(proxy, args);
            // 后置通知
            log.info("后置通知");
        } catch (Exception e) {
            // 异常通知
            log.error("{}异常通知", method.getName());
            throw e;
        }
        return result;
    }

}
